package com.wzw.basics.concurrent.lock.readwrite;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 独占锁（写时使用）：指该锁一次只能被一个线程所持有。对ReentrantLock和Synchronized而言都是独占锁
 * 共享锁（读时使用）：指该锁可被多个线程所持有
 * 读写锁（ReentrantReadWriteLock）：多个线程同时读一个资源类没有任何问题，所以为了满足并发量，读取共享资源应该可以同时进行。但是，如果有一个线程想去写共享资源，就不应该再有其它线程可以对该资源进行读或写。
 * @author Wangzhiwen
 */
public class ReadLockAndWriteLock {


    private static void write(Cache<String,String> cache){
        for (int i = 0; i < 50; i++) {
            final int temp = i;
            new Thread(() -> {
                cache.addCache(String.valueOf(temp), UUID.randomUUID().toString());
            },String.valueOf(i)).start();
        }
    }

    private static void read(Cache<String,String> cache){
        for (int i = 0; i < 50; i++) {
            final Integer temp = i;
            new Thread(() -> {
                cache.getCache(String.valueOf(temp));
            },String.valueOf(i)).start();
        }
    }

    public static void main(String[] args) {
        Cache<String, String> cache = new Cache<>();
        write(cache);
        read(cache);
    }

}

/**
 * 多线程对资源类进行读写操作
 * 不加读写锁时，读出来的数据与写入的不一致。这里使用读写锁解决，保证读时的并发性和写时的安全性
 */
class Cache<K,V> {

    private Map<K,V> cache = new HashMap<>();

    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public void addCache(K k , V v){
        readWriteLock.writeLock().lock();
        System.out.println("正在写入 : " + k);
        // 较慢
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            // ignore
        }
        cache.put(k,v);
        System.out.println("写入完成 : " + v);
        readWriteLock.writeLock().unlock();
    }

    public V getCache(K k){
        readWriteLock.readLock().lock();
        System.out.println("正在读取 : " + k);
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            // ignore
        }
        V v = cache.get(k);
        System.out.println("读取完成 : " + v);
        readWriteLock.readLock().unlock();
        return v;
    }

    public void clear(){
        cache.clear();
    }
}
